﻿#region Usings

using System;
using System.Collections;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;

#endregion

namespace TreeView
{
    [TemplatePart(Name = PartTreeItem, Type = typeof(TreeView))]
    [TemplatePart(Name = PartHeader, Type = typeof(ContentControl))]
    [TemplatePart(Name = PartItemsList, Type = typeof(ListBox))]
    [TemplateVisualState(GroupName = VisualGroupStates, Name = VisualStateExpanded)]
    [TemplateVisualState(GroupName = VisualGroupStates, Name = VisualStateCollapsed)]
    public class TreeViewItem: Control
    {
        #region Constants

        private const string PartHeader = "PART_Header";
        private const string PartItemsList = "PART_ItemsList";
        private const string PartTreeItem = "PART_Item";
        private const string VisualGroupStates = "States";
        private const string VisualStateCollapsed = "Collapsed";
        private const string VisualStateExpanded = "Expanded";

        #endregion

        #region Readonly & Static Fields

        public static readonly DependencyProperty HeaderTemplateProperty =
            DependencyProperty.Register("HeaderTemplate", typeof(object), typeof(TreeViewItem), new PropertyMetadata(default(DataTemplate)));

        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(TreeViewItem), new PropertyMetadata(default(IEnumerable)));

        #endregion

        #region Fields

        private Control _header;

        #endregion

        #region C'tors

        public TreeViewItem()
        {
            DefaultStyleKey = typeof(TreeViewItem);
        }

        #endregion

        #region Instance Properties

        public DataTemplate HeaderTemplate
        {
            get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
            set { SetValue(HeaderTemplateProperty, value); }
        }

        internal bool IsExpanded
        {
            get { return _isExpanded; }
            set
            {
                if(_isExpanded != value)
                {
                    _isExpanded = value;
                    _isHiden = !_isExpanded;
                    SetState(_isExpanded);
                }
            }
        }

       public int Deep { get; internal set; }
        private void SetState(bool isExpandedState)
        {

            if(isExpandedState)
            {
                if(Deep >= TreeView.LayoutRecursionLimit)
                {
                    return;
                }
                VisualStateManager.GoToState(this, VisualStateExpanded, false);

            }
            else
            {
                VisualStateManager.GoToState(this, VisualStateCollapsed, false);
                var itemsList = GetTemplateChild(PartItemsList) as ListBox;
                if(itemsList != null)
                    foreach(var treeviewitem in itemsList.Items
                                                         .Select(item => itemsList.ItemContainerGenerator.ContainerFromItem(item))
                                                         .Where(container => container != null)
                                                         .Select(container => ControlHelper.FindFirstElementInVisualTree<TreeViewItem>(container, PartTreeItem))
                                                         .Where(treeviewitem => treeviewitem != null))
                    {
                        treeviewitem.IsExpanded = false;
                    }
            }
        }

        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        #endregion

        #region Instance Methods

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            var itemsList = GetTemplateChild(PartItemsList) as ListBox;
            if(itemsList != null)
            {
                itemsList.LayoutUpdated += ItemsListLayoutUpdated;
            }
            _header = GetTemplateChild(PartHeader) as Control;
            if(_header != null)
            {
                _header.Tap += HeaderTap;
                _header.DoubleTap += HeaderTap;
            }
        }

        private void ItemsListLayoutUpdated(object sender, EventArgs e)
        {
            CheckContainer();
        }

        /// <summary>
        ///  CheckContainer content container
        /// </summary>
        private void CheckContainer()
        {
            var itemsList = GetTemplateChild(PartItemsList) as ListBox;
            if(itemsList != null && itemsList.Items.Any())
            {
                var c = itemsList.ItemContainerGenerator.ContainerFromIndex(0);
                if(c != null)
                {
                    Refresh(itemsList);
                } 
            }
        }

        #endregion

        #region Event Handling

        private void HeaderTap(object sender, GestureEventArgs e)
        {
            IsExpanded = !IsExpanded;
            OnSelected(Content);
        }

        private void Refresh(object sender)
        {
            var lb = sender as ListBox;

            if(lb != null)
            {
                foreach(var treeviewitem in lb.Items
                                              .Select(item => lb.ItemContainerGenerator.ContainerFromItem(item))
                                              .Where(container => container != null)
                                              .Select(container => ControlHelper.FindFirstElementInVisualTree<TreeViewItem>(container, PartTreeItem))
                                              .Where(treeviewitem => treeviewitem != null))
                {
                    treeviewitem.SetBinding(HeaderTemplateProperty, new Binding { Source = HeaderTemplate });

                    var sipbinding = GetBindingExpression(ItemsSourceProperty);
                    if(sipbinding != null)
                    {
                        treeviewitem.SetBinding(ItemsSourceProperty, sipbinding.ParentBinding);
                    }
                    treeviewitem.Deep = Deep + 1;
                    treeviewitem.Selected -= TreeviewitemSelected;
                    treeviewitem.Selected += TreeviewitemSelected;
                }
            }
        }

        private void TreeviewitemSelected(object sender, object e)
        {
            OnSelected(e);
        }

        #endregion

        internal event EventHandler<object> Selected;

        protected virtual void OnSelected(object e)
        {
            var handler = Selected;
            if(handler != null) handler(this, e);
        }

        #region Class Methods

        public object Content
        {
            get
            {
                var header = GetTemplateChild(PartHeader) as ContentControl;
                if(header != null)
                {
                    return header.Content;
                }
                return null;
            }
        }

        #endregion

        private bool _isHiden;
        private bool _isExpanded;

        public bool ExpandToItem(object item)
        {
            var itemsList = GetTemplateChild(PartItemsList) as ListBox;
            if(itemsList != null)
            {
                if (Deep >= TreeView.LayoutRecursionLimit)
                {
                    return false;
                }
                if(!IsExpanded)
                {
                    _isHiden = true;
                    VisualStateManager.GoToState(this, VisualStateExpanded, false);
                }
                foreach(var listBoxItem in itemsList.Items)
                {
                    if(listBoxItem == item)
                    {
                        Expand();
                        return true;
                    }

                    itemsList.UpdateLayout();
                    var container = itemsList.ItemContainerGenerator.ContainerFromItem(listBoxItem);

                    if(container != null)
                    {
                        var treeViewItem = ControlHelper.FindFirstElementInVisualTree<TreeViewItem>(container, PartTreeItem);
                        if(treeViewItem != null)
                        {
                            if(treeViewItem.ExpandToItem(item))
                            {
                                Expand();
                                return true;
                            }
                        }
                    }
                    
                }
                CheckHiddenState();
            }

            return false;
        }

        private void CheckHiddenState()
        {
            if(_isHiden)
            {
                VisualStateManager.GoToState(this, VisualStateCollapsed, false);
            }
        }

        private void Expand()
        {
            IsExpanded = true;
        }
    }
}